package com.ynet.middleground.approve.listener;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.activiti.engine.RuntimeService;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import com.ynet.core.exception.BusinessException;
import com.ynet.core.util.IDUtil;
import com.ynet.middleground.approve.cmd.SpringContextUtils;
import com.ynet.middleground.approve.constant.VotingVariables;
import com.ynet.middleground.approve.constant.WorkflowConstants;

/**
 * @author liulx
 * @description 多实例任务初始化设置一些自定义参数
 * @date 2019-11-26 13:38
 */
@Component
@DependsOn("springContextUtils")
public class InitMultiTaskListener implements TaskListener, VotingVariables {

    private RuntimeService runtimeService = SpringContextUtils.getBean(RuntimeService.class);

    /**
     * <h3>特别说明</h3> <br>
     * 该会签任务初始化监听器，会签节点的每一个任务实例，都会进入该方法初始化; <br>
     * 每一个任务实例都对应一个执行 id，且执行 id 是依次减 1
     * 
     * @param delegateTask 会签节点的实例任务
     * @author liulx
     */
    @Override
    public void notify(DelegateTask delegateTask) {
        // 获取流程id
        String exId = delegateTask.getExecutionId();
        // 初始化同意人数、不同意人数的变量
        runtimeService.setVariable(exId, NUMBER_OF_AGREED_INSTANCES, 0);
        runtimeService.setVariable(exId, NUMBER_OF_DISAGREED_INSTANCES, 0);

        // 获取当前执行 id
        long currentExid = Long.parseLong(delegateTask.getExecutionId());
        // 获取流程实例中，已记录的会签执行 id 列表
        // 执行 id 列表为 Map 集合，key 为会签节点 key 标识，value 为该会签节点的所有执行实例 id List 集合
        Object votingFlagValue = runtimeService.getVariable(exId, VOTING_EXIDS);
        // 若为空，表示该流程实例第一次执行会签任务
        if (votingFlagValue == null) {
            // 生成当前会签节点的实例任务的随机标识值
            String randomId = IDUtil.getSecureRandomId();
            // 添加会签任务随机标识到任务实例变量池中
            delegateTask.setVariableLocal(VOTING_FLAG, randomId);
            // 也一并将随机标识添加到流程流程变量池中，以供当前会签节点的其余任务实例使用
            runtimeService.setVariable(exId, VOTING_FLAG, randomId);
            // 封装会签节点的所有执行 id，并添加到流程实例中
            Map<String, List<Long>> voteMap = new HashMap<>(8);
            List<Long> votingExids = new ArrayList<>();
            votingExids.add(currentExid);
            voteMap.put(delegateTask.getTaskDefinitionKey(), votingExids);
            runtimeService.setVariable(exId, VOTING_EXIDS, voteMap);
        } else {
            // 如果已有记录，则转换为 Map
            if (votingFlagValue instanceof Map) {
                Map<String, List<Long>> voteMap = (Map<String, List<Long>>)votingFlagValue;
                // 取出当前会签节点的所有执行记录
                List<Long> votingExids = voteMap.get(delegateTask.getTaskDefinitionKey());
                // 如果为空，则说明是还未记录，则进行初始化
                if (votingExids == null || votingExids.isEmpty()) {
                    initializeVote(delegateTask);
                    votingExids = new ArrayList<>();
                    String randomId = IDUtil.getSecureRandomId();
                    delegateTask.setVariableLocal(VOTING_FLAG, randomId);
                    runtimeService.setVariable(exId, VOTING_FLAG, randomId);
                } else {
                    // 不为空说明已有记录，则取出最后一次记录的执行 id
                    Long lastExid = votingExids.get(votingExids.size() - 1);
                    // 最后一次记录的执行 id 和当前执行 id 进行差值判断
                    // 如果差 1，说明是本次和最后一次记录的为同一个会签节点的任务实例，否则不是同一个，则需要重新生成新的随机标识值
                    if ((lastExid - currentExid) == 1) {
                        delegateTask.setVariableLocal(VOTING_FLAG, runtimeService.getVariable(exId, VOTING_FLAG));
                    } else {
                        initializeVote(delegateTask);
                        String randomId = IDUtil.getSecureRandomId();
                        delegateTask.setVariableLocal(VOTING_FLAG, randomId);
                        runtimeService.setVariable(exId, VOTING_FLAG, randomId);
                    }
                }
                // 封装会签节点的所有执行 id，并添加到流程实例中
                votingExids.add(currentExid);
                voteMap.put(delegateTask.getTaskDefinitionKey(), votingExids);
                runtimeService.setVariable(exId, VOTING_EXIDS, voteMap);
            } else {
                throw new BusinessException("会签执行列表记录类型错误", "ECAP0508");
            }
        }
    }

    /**
     * 初始化计数变量
     * 
     * @param delegateTask
     */
    private void initializeVote(DelegateTask delegateTask) {
        String processInstanceId = delegateTask.getProcessInstanceId();
        runtimeService.removeVariable(processInstanceId, WorkflowConstants.EXCLUSIVE_GATEWAY_VARIABLE);
        runtimeService.setVariable(processInstanceId, NUMBER_OF_AGREED_INSTANCES, 0);
        runtimeService.setVariable(processInstanceId, NUMBER_OF_DISAGREED_INSTANCES, 0);
    }

}
